package main

import (
	"fmt"
	"os"
	"syscall"
)

// https://www.cnblogs.com/huxiao-tee/p/4660352.html
// 1、SIGBUS(Bus error)意味着指针所对应的地址是有效地址，但总线不能正常使用该指针。通常是未对齐的数据访问所致。
// 2、SIGSEGV(Segment fault)意味着指针所对应的地址是无效地址，没有物理内存对应该地址。

var (
	case3filepath = "/Users/xianmingliu/xsomnus/go/src/go-arch/linux/zerocopy/mmap/demo3/mmap_0.txt"
	case1filepath = "/Users/xianmingliu/xsomnus/go/src/go-arch/linux/zerocopy/mmap/demo3/mmap_5000.txt"
)

func main() {
	case2()
}

// case1 一个文件的大小是5000字节，mmap函数从一个文件的起始位置开始，映射5000字节到虚拟内存中
// 结论:
func case1() {
	file, _ := os.OpenFile(case1filepath, os.O_RDWR|os.O_CREATE, 0644)
	bytes, err := syscall.Mmap(int(file.Fd()), 0, 5000, syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		panic(err)
	}

	// 1. 操作0-5000字节范围的数据
	bytes[4999] = 'b'
	bytes[0] = 'b'

	// 2.1  读5000-8096范围内的数据
	s := string(bytes[13000])
	fmt.Println(s)
	// 2.2  读5000-8096范围内的数据
	bytes[5000] = 'm'
	bytes[8000] = 'j'

	bytes[10000] = 'z'

	if err = syscall.Munmap(bytes); err != nil {
		panic(err)
	}
}

// case2 一个文件的大小是5000字节，mmap函数从一个文件的起始位置开始，映射15000字节到虚拟内存中，即映射大小超过了原始文件的大小。
func case2() {
	file, _ := os.OpenFile(case1filepath, os.O_RDWR|os.O_CREATE, 0644)
	bytes, err := syscall.Mmap(int(file.Fd()), 0, 15000, syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		panic(err)
	}

	// 1. 操作0-5000字节范围的数据
	bytes[4999] = 'b'
	bytes[0] = 'b'

	// 2.1  读5000-8096范围内的数据
	s := string(bytes[8000])
	fmt.Println(s)
	// 2.2  读5000-8096范围内的数据
	bytes[5000] = 'm'
	bytes[8000] = 'v'

	bytes[8191] = 'z'
	if err = syscall.Munmap(bytes); err != nil {
		panic(err)
	}
}

// case3 一个文件初始大小为0，使用mmap操作映射了1000*4K的大小，即1000个物理页大约4M字节空间，mmap返回指针ptr。
func case3() {
	file, _ := os.OpenFile(case3filepath, os.O_RDWR|os.O_CREATE, 0644)

	bytes, err := syscall.Mmap(int(file.Fd()), 0, 1024*1024*4, syscall.PROT_WRITE, syscall.MAP_SHARED)
	if err != nil {
		panic(err)
	}
	fmt.Printf("length = %d, content= %s\n", len(bytes), string(bytes))

	// close file
	if err = file.Close(); err != nil {
		panic(err)
	}

	for i, c := range []byte("hello,mmap") {
		bytes[i] = c
	}

	bytes[1] = 'B'

	if err = syscall.Munmap(bytes); err != nil {
		panic(err)
	}
}
